
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <strings.h>
#include <stdio.h>
#include <vector>
#include "EfficientMap.cc"
#include "TwoWayList.cc"
#include "Types.cc"
#include "BlockSortedTempTable.h"


class LeftRecord;
class RightRecord;

static bool
Matches(const LeftRecord & left, const RightRecord & right);

static void
WriteOut(const LeftRecord & left, const RightRecord & right, FILE *toMe);

class RightRecord
{

private:

#include "AttsRight.cc"

  friend bool Matches(const LeftRecord & left, const RightRecord & right);
  friend void WriteOut(const LeftRecord & left, const RightRecord & right, FILE *outFile);
  friend class LeftRecord;

public:

  RightRecord  () {}
  ~RightRecord  () {}

  bool ReadIn(FILE *fromMe)
  {
    bool res;
#include "ReadInRight.cc"
    fgetc_unlocked (fromMe);
    return res;
  }

  void WriteBuffer(FILE *toMe)
  {
#include "WriteRightBuffer.cc"
    fputc_unlocked('\n', toMe);
  }

  bool operator < (const RightRecord & r2) const;
  bool operator < (const LeftRecord & r2) const;

};

class LeftRecord
{

private:

#include "AttsLeft.cc"

  friend bool Matches(const LeftRecord & left, const RightRecord & right);
  friend void WriteOut(const LeftRecord & left, const RightRecord & right, FILE *outFile);
  friend class RightRecord;
	
public:

  LeftRecord  () {}
  ~LeftRecord  () {}

  bool ReadIn(FILE *fromMe)
  {
    bool res;
#include "ReadInLeft.cc"
    fgetc_unlocked (fromMe);
    return res;
  }

  void WriteBuffer(FILE *toMe)
  {
#include "WriteLeftBuffer.cc"
    fputc_unlocked('\n', toMe);
  }

  bool operator < (const LeftRecord & r2) const;
  bool operator < (const RightRecord & r2) const;

};

bool
RightRecord::operator < (const RightRecord & r2) const
{
#include "RightCompareRight.cc"
    return false;
}

bool
RightRecord::operator < (const LeftRecord & r2) const
{
#include "RightCompareLeft.cc"
    return false;
}

bool
LeftRecord::operator < (const LeftRecord & r2) const
{
#include "LeftCompareLeft.cc"
    return false;
}

bool
LeftRecord::operator < (const RightRecord & r2) const
{
#include "LeftCompareRight.cc"
    return false;
}



static bool
Matches(const LeftRecord & left, const RightRecord & right)
{
#include "Predicate.cc"
}

static void
WriteOut(const LeftRecord & left, const RightRecord & right, FILE *toMe)
{
#include "WriteOut.cc"
  fputc_unlocked ('\n', toMe);
}

inline void
doJoin(LeftRecord & leftRecord, std::vector<RightRecord> & matchingRightRecords, FILE *fOut)
{
    for (std::vector<RightRecord>::iterator it = matchingRightRecords.begin();
	 it < matchingRightRecords.end(); ++it)
    {
	if (Matches(leftRecord, *it))
	{
	    WriteOut(leftRecord, *it, fOut);
	}
    }
}

int
main(int argc, char**argv)
{
    if (argc != 4)
    {
	fprintf(stderr, "Usage: TPJoin leftTable rightTable outFile.\n");
	exit(-1);
    }

    char *leftFile = argv[1];
    char *rightFile = argv[2];
    char *outFile = argv[3];

    BlockSortedTempTable<LeftRecord> leftBstt(leftFile);
    BlockSortedTempTable<RightRecord> rightBstt(rightFile);

    leftBstt.writeBlocks();
    printf("Finished leftBstt.writeBlocks().\n");
    rightBstt.writeBlocks();
    printf("Finished rightBstt.writeBlocks().\n");

    leftBstt.initForRead();
    rightBstt.initForRead();

    FILE *fOut = fopen(outFile, "w");

    LeftRecord leftRecord;
    RightRecord rightRecord;

    std::vector<RightRecord> matchingRightRecords;

    bool rightEmpty = false;
    bool readLeft = true;

    if (!rightBstt.next(rightRecord))
    {
	goto CLEANUP;
    }

    // Read the left records and right records until you find the first match.
    // Join the left record to the right records.
    // Read the rest of the left records that match the right records
    //   and join them to the right records.
    // Find the next matching left and right record and repeat.
    while (true)
    {
	if (readLeft == true)
	{
	    if (!leftBstt.next(leftRecord))
	    {
		goto CLEANUP;
	    }
	}

	// Read right records until right records >= leftRecord.
	while (rightRecord < leftRecord)
	{
	    if (!rightBstt.next(rightRecord))
	    {
		goto CLEANUP;
	    }
	}

	// Now rightRecord >= leftRecord
	// Advance leftRecords until they match rightRecord
	if (leftRecord < rightRecord)
	{
	    // Overshot rightRecord, so repeat loop to advance left records.
	    readLeft = true;
	    continue;
	}

	// At this point leftRecord == rightRecord

	matchingRightRecords.clear();
	matchingRightRecords.push_back(rightRecord);

	while (true)
	{
	  if (rightEmpty = !rightBstt.next(rightRecord))
	    {
	      break;
	    }

	  // Note no check for if the right is empty here because that will be
	  // detected the next iteration and joins are still necessary.
	  if (leftRecord < rightRecord)
	    {
	      break;
	    }
	  matchingRightRecords.push_back(rightRecord);
	}

	// Join the left record to the right records and write the output.
	doJoin(leftRecord, matchingRightRecords, fOut);

	while (true)
	{
	    if (!leftBstt.next(leftRecord))
	    {
		goto CLEANUP;
	    }

	    if (matchingRightRecords[0] < leftRecord)
	    {
		readLeft = false;
		break;
	    }

	    // Join the left record to the right records and write the output.
	    doJoin(leftRecord, matchingRightRecords, fOut);
	}

	// All of the right records that matched the left record at the start of this loop
	// have been joined to all of the left records that match as well.

	if (rightEmpty)
	{
	    goto CLEANUP;
	}
    }

CLEANUP:
    leftBstt.cleanup();
    rightBstt.cleanup();

    return 0;
}
